#include "LynxListener_C.hpp"
#include "LynxException_C.hpp"

namespace Lynx
{
namespace Audio
{

ALenum DM_Lynx_to_AL(LYNX_AUDIO_DISTANCE_MODEL model)
{
    switch(model)
    {
        case LADM_NONE:
            return AL_NONE;
        case LADM_INVERSE:
            return AL_INVERSE_DISTANCE;
        case LADM_LINEAR:
            return AL_LINEAR_DISTANCE;
        case LADM_LINEAR_CLAMPED:
            return AL_LINEAR_DISTANCE_CLAMPED;
        case LADM_EXPONENT:
            return AL_EXPONENT_DISTANCE;
        case LADM_EXPONENT_CLAMPED:
            return AL_EXPONENT_DISTANCE_CLAMPED;
        case LADM_INVERSE_CLAMPED:
            return AL_INVERSE_DISTANCE_CLAMPED;
        default:
            return -1;
    }
}

LYNX_AUDIO_DISTANCE_MODEL DM_AL_to_Lynx(ALenum model)
{
    switch(model)
    {
        case AL_NONE:
            return LADM_NONE;
        case AL_INVERSE_DISTANCE:
            return LADM_INVERSE;
        case AL_LINEAR_DISTANCE:
            return LADM_LINEAR;
        case AL_LINEAR_DISTANCE_CLAMPED:
            return LADM_LINEAR_CLAMPED;
        case AL_EXPONENT_DISTANCE:
            return LADM_EXPONENT;
        case AL_EXPONENT_DISTANCE_CLAMPED:
            return LADM_EXPONENT_CLAMPED;
        case AL_INVERSE_DISTANCE_CLAMPED:
            return LADM_INVERSE_CLAMPED;
        default:
            return LADM_NONE;
    }
}

void Listener_C::set_position(const Myth::Math::Vector3f &position)
{
    alListener3f(AL_POSITION, position.X, position.Y, position.Z);
}

Myth::Math::Vector3f Listener_C::get_position(void) const
{
    ALfloat v[3];
    alGetListenerfv(AL_POSITION, v);
    return Myth::Math::Vector3f(v[0], v[1], v[2]);
}

void Listener_C::set_velocity(const Myth::Math::Vector3f &velocity)
{
    alListener3f(AL_VELOCITY, velocity.X, velocity.Y, velocity.Z);
}

Myth::Math::Vector3f Listener_C::get_velocity(void) const
{
    ALfloat v[3];
    alGetListenerfv(AL_VELOCITY, v);
    return Myth::Math::Vector3f(v[0], v[1], v[2]);
}

void Listener_C::set_up_vector(const Myth::Math::Vector3f &up)
{
    set_orientation(get_target(), up);
}

Myth::Math::Vector3f Listener_C::get_up_vector(void) const
{
    ALfloat v[6];
    alGetListenerfv(AL_ORIENTATION, v);
    return Myth::Math::Vector3f(v[3], v[4], v[5]);
}

void Listener_C::set_target(const Myth::Math::Vector3f &target)
{
    set_orientation(target, get_up_vector());
}

Myth::Math::Vector3f Listener_C::get_target(void) const
{
    ALfloat v[6];
    alGetListenerfv(AL_ORIENTATION, v);
    return Myth::Math::Vector3f(v[0], v[1], v[2]);
}

void Listener_C::set_orientation(const Myth::Math::Vector3f &target, const Myth::Math::Vector3f &up)
{
    ALfloat v[6];
    v[0] = target.X;
    v[1] = target.Y;
    v[2] = target.Z;
    v[3] = up.X;
    v[4] = up.Y;
    v[5] = up.Z;
    alListenerfv(AL_ORIENTATION, v);
}

void Listener_C::set_volume(float volume)
{
    if(volume < 0.0f)
        THROW_EXCEPTION(LEC_PARAMETER_OUT_OF_RANGE, "The volume can't be negative.");

    alListenerf(AL_GAIN, volume);
}

float Listener_C::get_volume(void) const
{
    ALfloat v;
    alGetListenerf(AL_GAIN, &v);
    return v;
}

void Listener_C::set_doppler_factor(float factor)
{
    if(factor < 0.0f)
        THROW_EXCEPTION(LEC_PARAMETER_OUT_OF_RANGE, "The doppler factor can't be negative.");

    alDopplerFactor(factor);
}

float Listener_C::get_doppler_factor(void) const
{
    return alGetFloat(AL_DOPPLER_FACTOR);
}

void Listener_C::set_speed_of_sound(float speed)
{
    if(speed <= 0.0f)
        THROW_EXCEPTION(LEC_PARAMETER_OUT_OF_RANGE, "The speed of sound can't be zero or less.");

    alSpeedOfSound(speed);
}

float Listener_C::get_speed_of_sound(void) const
{
    return alGetFloat(AL_SPEED_OF_SOUND);
}

void Listener_C::set_distance_model(LYNX_AUDIO_DISTANCE_MODEL model)
{
    ALenum dm = DM_Lynx_to_AL(model);

    if(dm == -1)
        THROW_EXCEPTION(LEC_INVALID_PARAMETER, "Not a valid distance model.");

    alDistanceModel(dm);
}

LYNX_AUDIO_DISTANCE_MODEL Listener_C::get_distance_model(void) const
{
    return DM_AL_to_Lynx(alGetInteger(AL_DISTANCE_MODEL));
}

} // namespace Audio
} // namespace Lynx

